package bancosys.tec.persist.uniquevalue.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.UniqueConstraint;

import org.hibernate.LockMode;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.persist.controller.TransactionalController;
import bancosys.tec.persist.persister.annotation.NaturalKey;
import bancosys.tec.persist.persister.annotation.SystemId;

/**
 * Entidade que representa um valor de referencia para geração de números únicos sequenciais.
 * 
 * @author lundberg
 */
@Entity
@Table(name = "UNIQUE_VALUE_REFERENCE")
@Documentation("CONTROLE DA GERACAO DE NUMEROS UNICOS SEQUENCIAIS UTILIZADOS EM OUTRAS TABELAS. ")
@Alias("UNVARE")
@SequenceGenerator(name = "SEQ_UNVARE", sequenceName = "SEQ_UNVARE")
public class UniqueValueReference extends PersistableBusinessObject {

    private Long id;

    private String mnemonico;

    private Long nextVal;

    /**
     * @return the id
     */
    @Id
    @SystemId
    @Column(name = "COD_UNIQ_VL_REF")
    @GeneratedValue(generator = "SEQ_UNVARE")
    @Documentation("CODIGO DO CONTROLE DE SEQUENCIAL.")
    public Long getId() {
        return this.id;
    }

    /**
     * @return the mnemonico
     */
    @NaturalKey
    @Column(name = "MNE_UNIQ_VL_REF", nullable = false)
    @Documentation("MNEMONICO DA ENTIDADE QUE UTILIZA A GERACAO DE SEQUENCIAL.")
    @UniqueConstraint(suffix = "0")
    public String getMnemonico() {
        return this.mnemonico;
    }

    /**
     * @return the nextVal
     */
    @Column(name = "NEXT_VALUE", nullable = false)
    @Documentation("PROXIMO VALOR A SER UTILIZADO COMO SEQUENCIAL.")
    public Long getNextVal() {
        return this.nextVal;
    }

    /**
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @param mnemonico the mnemonico to set
     */
    public void setMnemonico(String mnemonico) {
        this.mnemonico = mnemonico;
    }

    /**
     * @param nextVal the nextVal to set
     */
    public void setNextVal(Long nextVal) {
        this.nextVal = nextVal;
    }

    /**
     * Obtém o próximo valor dessa sequence, criando uma transação própria para isso.
     * 
     * @return valor gerado único sequencial
     */
    public Long transactedNextValue() {
        TransactionalController controller = this.getController();
        // Processo deve ocorrer dentro de uma transação para que o lock seja liberado o quanto antes
        controller.startTransaction();
        boolean error = true;
        try {
            Long next = this.nextValue();
            error = false;
            return next;
        } finally {
            if (error) {
                controller.rollback();
            } else {
                controller.commit();
            }
        }
    }

    /**
     * Obtém o próximo valor da sequence
     * 
     * @return valor gerado único sequencial
     */
    public Long nextValue() {
        // select for update
        this.getController().getSessionFactory().getCurrentSession().refresh(this, LockMode.UPGRADE);

        Long next = this.getNextVal();
        this.setNextVal(next + 1L);
        // save com valor incrementado
        this.getPersister().save();
        return next;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transient
    public TransactionalController getController() {
        return (TransactionalController) super.getController();
    }
}
